/* debug related codes were copied from D-Bus */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>

#include "imbus_internal.h"

static IMBool __im_warn_initted                   = FALSE;
static IMBool __im_fatal_warnings                 = FALSE;
static IMBool __im_fatal_warnings_on_check_failed = TRUE;

static void
__init_warnings(void)
{
    const char *s;

    if (__im_warn_initted) return;

    s = getenv ("IMBUS_FATAL_WARNINGS");

    if (s && *s) {
        if (*s == '0') {
            __im_fatal_warnings                 = FALSE;
            __im_fatal_warnings_on_check_failed = FALSE;
        } else if (*s == '1') {
            __im_fatal_warnings                 = TRUE;
            __im_fatal_warnings_on_check_failed = TRUE;
        } else {
            fprintf(stderr, "IMBUS_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'", s);
        }
    }
    __im_warn_initted = TRUE;
}

void
_im_warn (const char *format, ...)
{
    va_list args;

    if (!__im_warn_initted)
        __init_warnings ();
  
    va_start (args, format);
    vfprintf (stderr, format, args);
    va_end (args);

    if (__im_fatal_warnings) {
        fflush (stderr);
        abort ();
    }
}

void
_im_warn_check_failed(const char *format, ...)
{
    va_list args;
  
    if (!__im_warn_initted)
        __init_warnings ();

    fprintf (stderr, "process %u: ", getpid ());
  
    va_start (args, format);
    vfprintf (stderr, format, args);
    va_end (args);

    if (__im_fatal_warnings_on_check_failed) {
        fflush (stderr);
        abort ();
    }
}

#ifdef IM_ENABLE_VERBOSE_MODE

static IMBool   __im_verbose_initted = FALSE;
static IMBool   __im_verbose = TRUE;

static inline void
__im_verbose_init (void)
{
    if (!__im_verbose_initted) {
      const char *p = getenv ("IMBUS_VERBOSE"); 
      __im_verbose = (p != NULL && *p == '1');
      __im_verbose_initted = TRUE;
    }
}

/**
 * Implementation of _im_is_verbose() macro if built with verbose logging
 * enabled.
 * @returns whether verbose logging is active.
 */
IMBool
_im_is_verbose_real (void)
{
  __im_verbose_init ();
  return __im_verbose;
}

/**
 * Prints a warning message to stderr
 * if the user has enabled verbose mode.
 * This is the real function implementation,
 * use _im_verbose() macro in code.
 *
 * @param format printf-style format string.
 */
void
_im_verbose_real (const char *format, ...)
{
    va_list args;
    static IMBool need_pid = TRUE;
    int len;
  
    /* things are written a bit oddly here so that
     * in the non-verbose case we just have the one
     * conditional and return immediately.
     */
    if (!_im_is_verbose_real())
        return;

    /* Print out pid before the line */
    if (need_pid)
        fprintf (stderr, "%u: ", getpid ());
      

    /* Only print pid again if the next line is a new line */
    len = strlen (format);
    if (format[len-1] == '\n')
        need_pid = TRUE;
    else
        need_pid = FALSE;
  
    va_start (args, format);
    vfprintf (stderr, format, args);
    va_end (args);

    fflush (stderr);
}

#endif /* IM_ENABLE_VERBOSE_MODE */

#ifndef IM_DISABLE_CHECKS
/** String used in _im_return_if_fail macro */
const char _im_return_if_fail_warning_format[] =
"arguments to %s() were incorrect, assertion \"%s\" failed in file %s line %d.\n"
"This is normally a bug in some application using the IMBUS library.\n";
#endif

#ifndef IM_DISABLE_ASSERT
/**
 * Internals of _im_assert(); it's a function
 * rather than a macro with the inline code so
 * that the assertion failure blocks don't show up
 * in test suite coverage, and to shrink code size.
 *
 * @param condition TRUE if assertion succeeded
 * @param condition_text condition as a string
 * @param file file the assertion is in
 * @param line line the assertion is in
 * @param func function the assertion is in
 */
void
_im_real_assert   (IMBool       condition,
                   const char  *condition_text,
                   const char  *file,
                   int          line,
                   const char  *func)
{
    if (!condition) {
        _im_warn ("%u: assertion failed \"%s\" file \"%s\" line %d function %s\n",
                  getpid (), condition_text, file, line, func);
        abort ();
    }
}

/**
 * Internals of _im_assert_not_reached(); it's a function
 * rather than a macro with the inline code so
 * that the assertion failure blocks don't show up
 * in test suite coverage, and to shrink code size.
 *
 * @param explanation what was reached that shouldn't have been
 * @param file file the assertion is in
 * @param line line the assertion is in
 */
void
_im_real_assert_not_reached   (const char *explanation,
                               const char *file,
                               int         line)
{
    _im_warn ("File \"%s\" line %d process %u should not have been reached: %s\n",
              file, line, getpid (), explanation);
    abort ();
}
#endif /* IM_DISABLE_ASSERT */

/*
vi:ts=4:nowrap:ai:expandtab
*/
